package il.ac.bgu.cs.heuristics.graph.pddl.parser.types;

import il.ac.bgu.cs.heuristics.graph.pddl.PddlUtils;
import il.ac.bgu.cs.heuristics.graph.pddl.parser.ExpressionParser;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * An immutable representation of an action in a pddl.
 * 
 * 
 * @author Ronen Pinko
 * 
 */
public class Action {

	private final int						arity;
	private final LogicalExpression			precondition;
	private final LogicalExpression			effect;
	private final List<AbstractParameter>	parameters;
	private final String					name;

	public Action(pddl4j.exp.action.Action pddlAction) {
		this.name = pddlAction.getName();
		this.parameters = PddlUtils.extractActionParameters(pddlAction);
		this.arity = parameters.size();
		final String preconditionString = fixPredicateTyping(pddlAction
				.getPrecondition().toString(), parameters);
		this.precondition = ExpressionParser
				.parseLogicalExpression(preconditionString);
		final String effectString = fixPredicateTyping(pddlAction.getEffect()
				.toString(), parameters);
		this.effect = ExpressionParser.parseLogicalExpression(effectString);
	}

	private static String fixPredicateTyping(String predicateStr,
			List<AbstractParameter> parameters) {
		String result = predicateStr;
		for (AbstractParameter param : parameters) {
			final String paramName = param.name();
			final String types = typesToString(param.getPossibleTypes());
			result = replaceParamToTyped(result, paramName, types);
		}
		return result;
	}

	private static String replaceParamToTyped(String str, String paramName,
			String types) {

		String result = str.replace(paramName + " ", paramName + " - " + types
				+ " ");
		result = result.replace(paramName + ")", paramName + " - " + types
				+ ")");
		return result;
	}

	private static String typesToString(Set<Type> possibleTypes) {
		if (possibleTypes.size() > 1) {
			final StringBuilder sb = new StringBuilder("(either ");

			for (final Iterator<Type> typeIt = possibleTypes.iterator(); typeIt
					.hasNext();) {
				sb.append(typeIt.next().name());
				if (typeIt.hasNext()) {
					sb.append(' ');
				}
			}
			sb.append(')');
			return sb.toString();
		} else {
			return possibleTypes.iterator().next().name();
		}
	}

	public String name() {
		return name;
	}

	public int arity() {
		return arity;
	}

	public LogicalExpression getPrecondition() {
		return precondition;
	}

	public LogicalExpression getEffect() {
		return effect;
	}

	public List<AbstractParameter> getParameters() {
		return Collections.unmodifiableList(parameters);
	}

	@Override
	public String toString() {
		return "Action [" + name + "(" + parameters + "){" + precondition
				+ "->" + effect + "}]";
	}

}
